
#include "Game.h"
#include "../../SGD Wrappers/CSGD_Direct3D.h"
#include "../../SGD Wrappers/CSGD_DirectInput.h"
#include "../../SGD Wrappers/CSGD_TextureManager.h"
#include "../../SGD Wrappers/CSGD_XAudio2.h"
#include "BitmapFont.h"
#include "MainMenuState.h"
#include <cassert>


//Singleton accessor
Game* Game::GetInstance()
{ 
	static Game instance;
	return &instance;
}


//Constructor
Game::Game(void)
{
	srand( (unsigned int)GetTickCount() );
	rand();
	m_pD3D				= nullptr;
	m_pDI				= nullptr;
	m_pTM				= nullptr;
	m_pXA				= nullptr;
	m_pBF				= nullptr;
	m_pCurrState		= nullptr;
	m_nScreenWidth		= 1;
	m_nScreenHeight		= 1;
	m_bIsWindowed		= true;
	m_dwCurrTime		= 0;
	m_unFPS				= 0;
	m_unFrames			= 0;
	m_fFPSTimer			= 0.0f;
}


//Destructor
Game::~Game(void)
{
}


//Initialize
void Game::Initialize(HWND hWnd, HINSTANCE hInstance, int nWidth, int nHeight, bool bWindowed)
{
	// Store the SGD Wrapper singletons
	m_pD3D	= CSGD_Direct3D::GetInstance();
	m_pDI	= CSGD_DirectInput::GetInstance();
	m_pTM	= CSGD_TextureManager::GetInstance();
	m_pXA	= CSGD_XAudio2::GetInstance();

	// Store the parameters
	m_nScreenWidth	= nWidth;
	m_nScreenHeight	= nHeight;
	m_bIsWindowed	= bWindowed;

	// Initialize the SGD Wrappers
	m_pD3D->Initialize(hWnd, m_nScreenWidth, m_nScreenHeight, m_bIsWindowed, false);
	m_pDI->Initialize(hWnd, hInstance, DI_KEYBOARD | DI_MOUSE /*| DI_JOYSTICKS*/);
	m_pTM->Initialize(m_pD3D->GetDirect3DDevice(), m_pD3D->GetSprite());
	m_pXA->Initialize();

	// Initialize the BitmapFont
	m_pBF = new CBitmapFont;
	m_pBF->Initialize();

	//Load background image
	backgroundImageID = CSGD_TextureManager::GetInstance()->LoadTexture(_T("../resource/graphics/PlaceholderImage.png") );

	musicID = CSGD_XAudio2::GetInstance()->MusicLoadSong(_T("../resource/sounds/PlaceholderMusic.xwm"));
	CSGD_XAudio2::GetInstance()->MusicPlaySong(musicID, true);

	// Start the game in the Main Menu State
	ChangeState(MainMenuState::GetInstance());

	// Store the current time
	m_dwCurrTime = GetTickCount();

	// Reset the FPS
	m_unFPS		= 60;
	m_unFrames	= 0;
	m_fFPSTimer	= 0.0f;
}


//Run one frame of the game
bool Game::Main(void)
{
	if (Input() == false)
		return false;

	Update();
	Render();
	return true;
}


//Clean up memory and shutdown the SGD wrappers
void Game::Terminate(void)
{
	// Exit the current state
	ChangeState(nullptr);

	// Shutdown the BitmapFont
	m_pBF->Terminate();
	delete m_pBF;
	m_pBF = nullptr;

	//Unload background image
	CSGD_TextureManager::GetInstance()->UnloadTexture(backgroundImageID);
	backgroundImageID = -1;

	//Unload music
	CSGD_XAudio2::GetInstance()->MusicStopSong(musicID);
	CSGD_XAudio2::GetInstance()->MusicUnloadSong(musicID);
	musicID = -1;

	// Shutdown the wrappers in REVERSE ORDER!
	m_pXA->Terminate();
	m_pTM->Terminate();
	m_pDI->Terminate();
	m_pD3D->Terminate();

	// Reset ALL the data members
	m_pD3D			= nullptr;
	m_pDI			= nullptr;
	m_pTM			= nullptr;
	m_pXA			= nullptr;	

	m_nScreenWidth	= 1;
	m_nScreenHeight	= 1;
	m_bIsWindowed	= true;
}


//Send input to the current state
bool Game::Input(void)
{
	//Read the devices
	m_pDI->ReadDevices();

	//Alt+Enter to toggle fullscreen mode
	if ((m_pDI->KeyDown(DIK_LALT) == true || m_pDI->KeyDown(DIK_RALT) == true)
		&& m_pDI->KeyPressed(DIK_RETURN) == true)
	{
		static bool windowed = m_bIsWindowed;
		windowed = !windowed;
		m_pD3D->Resize(800,600, windowed);
	}

	//Let the current state handle input
	return m_pCurrState->Input();
}


//Update
void Game::Update(void)
{
	//Update the audio
	m_pXA->Update();

	//Calculate the elapsed time between frames
	DWORD dwNow = GetTickCount();

	float fElapsedTime = (dwNow - m_dwCurrTime) / 1000.0f;

	//Cap the elapsed time to 1/8th of a second
	if (fElapsedTime > 0.125f)
		fElapsedTime = 0.125f;

	//Store the current time
	m_dwCurrTime = dwNow;

	//Update the FPS
	m_fFPSTimer += fElapsedTime;
	m_unFrames++;

	if (m_fFPSTimer > 1.0f)	// 1 sec
	{
		m_fFPSTimer = 0.0f;
		m_unFPS		= m_unFrames;
		m_unFrames	= 0;
	}

	//Update the current state
	m_pCurrState->Update(fElapsedTime);
}


//Render
void Game::Render(void)
{
	//Clear the screen
	m_pD3D->Clear(D3DCOLOR_XRGB(50, 50, 50));

	//Start D3D rendering
	m_pD3D->DeviceBegin();
	m_pD3D->SpriteBegin();
	{
		//Draw background image
		RECT rect = {0,0,1024,768};
		CSGD_TextureManager::GetInstance()->Draw(
			backgroundImageID, 0, 0, 
			1.0f, 1.0f, &rect,
			0.0f, 0.0f, 0.0f,
			D3DCOLOR_XRGB(0, 0, 0));

		//Render the current state
		m_pCurrState->Render();
		//Render the FPS
		TOSTRINGSTREAM output;
		output << _T("FPS: ") << m_unFPS;
		//m_pD3D->DrawText( output.str().c_str(), 10, 10 );
		m_pBF->Draw(output.str().c_str(), GetScreenWidth() - 150, 20, 0.6f, D3DCOLOR_ARGB(150, 255, 255, 255));
		//m_pBF->Draw( _T("1\t2\t3\t4\nABCDEFabcdef\n7"), 42, 300, 2.5f, D3DCOLOR_ARGB( 127, 127, 0, 0 ) );
	}
	//End the D3D rendering
	m_pD3D->SpriteEnd();
	m_pD3D->DeviceEnd();
	//Present the backbuffer to the screen
	m_pD3D->Present();
}


//Change state
void Game::ChangeState(State* pNewState)
{
	//Exit the existing state
	if(m_pCurrState != nullptr)
		m_pCurrState->Exit();

	//Store the new state
	m_pCurrState = pNewState;

	//Enter the new state
	if(m_pCurrState != nullptr)
		m_pCurrState->Enter();
}
